package com.fastbee.mq.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fastbee.common.core.mq.InvokeReqDto;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.iot.domain.Alert;
import com.fastbee.iot.domain.AlertLog;
import com.fastbee.iot.domain.AlertTrigger;
import com.fastbee.iot.domain.Scene;
import com.fastbee.iot.domain.SceneTrigger;
import com.fastbee.iot.mapper.AlertLogMapper;
import com.fastbee.iot.mapper.AlertMapper;
import com.fastbee.iot.mapper.AlertTriggerMapper;
import com.fastbee.iot.mapper.DeviceMapper;
import com.fastbee.iot.mapper.SceneMapper;
import com.fastbee.iot.mapper.SceneTriggerMapper;
import com.fastbee.iot.model.Action;
import com.fastbee.iot.model.DeviceRelateAlertLogVO;
import com.fastbee.iot.model.TriggerParameter;
import com.fastbee.mq.model.ReportDataBo;
import com.fastbee.mq.service.IFunctionInvoke;
import com.fastbee.mq.service.IMqttMessagePublish;
import com.fastbee.mq.service.IRuleEngine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.regex.Pattern.compile;

/**
 * 规则引擎处理数据方法
 * @author bill
 */
@Component
@Slf4j
public class RuleEngineHandler implements IRuleEngine {


    @Resource
    private AlertTriggerMapper alertTriggerMapper;
    @Resource
    private AlertMapper alertMapper;
    @Resource
    private AlertLogMapper alertLogMapper;
    @Resource
    private SceneMapper sceneMapper;
    @Resource
    private SceneTriggerMapper sceneTriggerMapper;
    @Resource
    private IMqttMessagePublish messagePublish;
    @Resource
    private IFunctionInvoke functionInvoke;
    @Resource
    private DeviceMapper deviceMapper;

    /**
     * 规则匹配(告警和场景联动)
     * @param bo 上报数据模型bo
     * @see ReportDataBo
     */
    public void ruleMatch(ReportDataBo bo) {
        try {
            // 告警处理
            this.alertProcess(bo);
            // 场景联动处理
            this.sceneProcess(bo);

        } catch (Exception e) {
            log.error("接收数据，解析数据时异常 message={}",e, e.getMessage());
        }
    }



    /**
     * 场景处理
     */
    public void sceneProcess(ReportDataBo bo) {
        TriggerParameter sceneTriggerParameter = new TriggerParameter();
        sceneTriggerParameter.setSerialNumber(bo.getSerialNumber());
        sceneTriggerParameter.setStatus(1);
        int type = bo.getType();
        if (type == 1 || type == 2 || type == 3) {
            List<ThingsModelSimpleItem> thingsModelSimpleItems=bo.getDataList();
            if(CollectionUtils.isEmpty(bo.getDataList())|| bo.getDataList().size()==0){
                thingsModelSimpleItems=JSON.parseArray(bo.getMessage(),ThingsModelSimpleItem.class);
            }
            //List<ThingsModelSimpleItem> thingsModelSimpleItems = JSON.parseArray(bo.getMessage(), ThingsModelSimpleItem.class);
            String[] ids = thingsModelSimpleItems.stream().map(ThingsModelSimpleItem::getId).toArray(String[]::new);
            sceneTriggerParameter.setIds(ids);
            List<SceneTrigger> sceneTriggers = sceneTriggerMapper.selectSceneTriggerList(sceneTriggerParameter);
            for (int i = 0; i < thingsModelSimpleItems.size(); i++) {
                for (int j = 0; j < sceneTriggers.size(); j++) {
                    if (thingsModelSimpleItems.get(i).getId().equals(sceneTriggers.get(j).getId())) {
                        // 获取匹配结果
                        boolean result = ruleResult(sceneTriggers.get(j).getOperator(),
                                thingsModelSimpleItems.get(i).getValue(),
                                sceneTriggers.get(j).getValue());
                        if (result) {
                            // 查询场景信息并执行动作
                            Scene scene = sceneMapper.selectSceneBySceneId(sceneTriggers.get(j).getSceneId());
                            sceneActionProcess(scene, type);
                        }

                    }
                }
            }
        } else {
            // 上线和下线匹配
            log.info("上下线触发[{}]-----------------------------------------",type==5?"上线":"离线");
            sceneTriggerParameter.setType(type);
            List<SceneTrigger> sceneTriggers = sceneTriggerMapper.selectSceneTriggerList(sceneTriggerParameter);
            if (sceneTriggers.size() > 0) {
                // 查询场景信息并执行动作
                Scene scene = sceneMapper.selectSceneBySceneId(sceneTriggers.get(0).getSceneId());
                sceneActionProcess(scene, type);
            }
        }
    }

    /**
     * 场景联动动作处理
     *
     * @param scene
     */
    private void sceneActionProcess(Scene scene, int type) {
        log.info("执行场景联动动作-----------------------------------------");
        List<Action> actions = JSON.parseArray(scene.getActions(), Action.class);
        for (Action action : actions) {
            InvokeReqDto reqDto = new InvokeReqDto();
            reqDto.setProductId(action.getProductId());
            reqDto.setSerialNumber(action.getSerialNumber());
            reqDto.setModelName(action.getName());
            reqDto.setType(1);
            reqDto.setIdentifier(action.getId());
            Map<String,Object> params = new HashMap<>();
            params.put(action.getId(),action.getValue());
            reqDto.setRemoteCommand(params);
            reqDto.setValue(new JSONObject(reqDto.getRemoteCommand()));
            functionInvoke.invokeNoReply(reqDto);
        }
        //for (int k = 0; k < actions.size(); k++) {
        //    ThingsModelSimpleItem model = new ThingsModelSimpleItem();
        //    model.setId(actions.get(k).getId());
        //    model.setValue(actions.get(k).getValue());
        //    model.setRemark("场景联动触发");
        //    // 设备上线，延迟3秒发布，等待设备完成订阅，后面可修改webhook事件类型解决
        //    if (actions.get(k).getType() == 1) {
        //        List<ThingsModelSimpleItem> propertys = new ArrayList<>();
        //        propertys.add(model);
        //       messagePublish.publishProperty(actions.get(k).getProductId(), actions.get(k).getSerialNumber(), propertys, type == 5 ? 3 : 0);
        //    } else if (actions.get(k).getType() == 2) {
        //        List<ThingsModelSimpleItem> functions = new ArrayList<>();
        //        functions.add(model);
        //        messagePublish.publishFunction(actions.get(k).getProductId(), actions.get(k).getSerialNumber(), functions, type == 5 ? 3 : 0);
        //    }
        //}
    }

    /**
     * 告警处理
     *
     */
    public void alertProcess(ReportDataBo bo) {
        TriggerParameter triggerParameter = new TriggerParameter();
        triggerParameter.setProductId(bo.getProductId());
        triggerParameter.setStatus(1);
        triggerParameter.setSource(1);
        int type = bo.getType();
        if (type == 1 || type == 2 || type == 3) {
            // 待测试
            List<ThingsModelSimpleItem> thingsModelSimpleItems=bo.getDataList();
            if(CollectionUtils.isEmpty(bo.getDataList())|| bo.getDataList().size()==0){
                thingsModelSimpleItems=JSON.parseArray(bo.getMessage(),ThingsModelSimpleItem.class);
            }
//            List<ThingsModelSimpleItem> thingsModelSimpleItems = JSON.parseArray(bo.getMessage(), ThingsModelSimpleItem.class);
            String[] ids = thingsModelSimpleItems.stream().map(ThingsModelSimpleItem::getId).toArray(String[]::new);
            triggerParameter.setIds(ids);
            List<AlertTrigger> alertTriggers = alertTriggerMapper.selectAlertTriggerList(triggerParameter);
            for (int i = 0; i < thingsModelSimpleItems.size(); i++) {
                for (int j = 0; j < alertTriggers.size(); j++) {
                    if (thingsModelSimpleItems.get(i).getId().equals(alertTriggers.get(j).getId())) {
                        // 获取匹配结果
                        boolean result = ruleResult(alertTriggers.get(j).getOperator(),
                                thingsModelSimpleItems.get(i).getValue(),
                                alertTriggers.get(j).getValue());
                        if (result) {
                            // 查询告警信息并执行动作
                            thingsModelSimpleItems.get(i).setRemark("设备触发");
                            Alert alert = alertMapper.selectAlertByAlertId(alertTriggers.get(j).getAlertId());
                            alertActionProcess(alert, bo, JSON.toJSONString(thingsModelSimpleItems.get(i)), type);
                        }
                    }
                }
            }
        } else {
            // 上线和下线匹配
            triggerParameter.setType(type);
            List<AlertTrigger> alertTriggers = alertTriggerMapper.selectAlertTriggerList(triggerParameter);
            if (alertTriggers.size() > 0) {
                // 查询告警信息并执行动作
                Alert alert = alertMapper.selectAlertByAlertId(alertTriggers.get(0).getAlertId());
                String jsonDetail = "";
                if (type == 5) {
                    jsonDetail = "{\"id\":\"online\",\"value\":\"设备上线\",\"remark\":\"设备触发\"}";
                } else if (type == 6) {
                    jsonDetail = "{\"id\":\"offline\",\"value\":\"设备下线\",\"remark\":\"设备触发\"}";
                }
                // 告警动作处理
                alertActionProcess(alert, bo, jsonDetail, type);
            }
        }
    }

    /**
     * 告警动作处理
     *
     * @param alert
     * @param jsonDetail
     */
    private void alertActionProcess(Alert alert, ReportDataBo bo, String jsonDetail, int type) {
        log.info("执行告警动作-----------------------------------------");
        List<Action> actions = JSON.parseArray(alert.getActions(), Action.class);
        for (Action action : actions) {
            InvokeReqDto reqDto = new InvokeReqDto();
            reqDto.setProductId(action.getProductId());
            reqDto.setSerialNumber(bo.getSerialNumber());
            reqDto.setModelName(action.getName());
            reqDto.setType(1);
            reqDto.setIdentifier(action.getId());
            Map<String,Object> params = new HashMap<>();
            params.put(action.getId(),action.getValue());
            reqDto.setRemoteCommand(params);
            reqDto.setValue(new JSONObject(reqDto.getRemoteCommand()));
            functionInvoke.invokeNoReply(reqDto);
        }
        //for (int k = 0; k < actions.size(); k++) {
        //    ThingsModelSimpleItem model = new ThingsModelSimpleItem();
        //    model.setId(actions.get(k).getId());
        //    model.setValue(actions.get(k).getValue());
        //    model.setRemark("告警触发");
        //    if (actions.get(k).getType() == 1) {
        //        propertys.add(model);
        //    } else if (actions.get(k).getType() == 2) {
        //        functions.add(model);
        //    }
        //}
        //// 发布属性，设备上线需要延迟3秒，等待设备订阅主题
        //if (propertys.size() > 0) {
        //    messagePublish.publishProperty(productId, deviceNum, propertys, type == 5 ? 3 : 0);
        //}
        //// 发布功能
        //if (functions.size() > 0) {
        //    messagePublish.publishFunction(productId, deviceNum, functions, type == 5 ? 3 : 0);
        //}
        // 生成告警消息通知，TODO 其他通知方式
        AlertLog alertLog = new AlertLog();
        alertLog.setAlertName(alert.getAlertName());
        alertLog.setAlertLevel(alert.getAlertLevel());
        if (alert.getAlertLevel() == 1) {
            // 1=不需要处理,2=未处理,3=已处理
            alertLog.setStatus(1);
        } else {
            alertLog.setStatus(2);
        }
        alertLog.setProductId(bo.getProductId());
        alertLog.setSerialNumber(bo.getSerialNumber());
        alertLog.setDetail(jsonDetail);
        alertLog.setUserId(bo.getUserId());
        alertLog.setCreateTime(DateUtils.getNowDate());
        alertLog.setDeviceName(bo.getDeviceName());
        // 兜底设备名称和用户id
        if (alertLog.getUserId() == null || StringUtils.isEmpty(alertLog.getDeviceName())) {
            DeviceRelateAlertLogVO deviceRelateAlertLogVO = deviceMapper.selectRelateAlertLogBySerialNumber(alertLog.getSerialNumber());
            if (deviceRelateAlertLogVO != null) {
                alertLog.setUserId(deviceRelateAlertLogVO.getUserId());
                alertLog.setDeviceName(deviceRelateAlertLogVO.getDeviceName());
            }
        }
        alertLogMapper.insertAlertLog(alertLog);
    }

    /**
     * 规则匹配结果
     *
     * @param operator     操作符
     * @param value        上报的值
     * @param triggerValue 触发器的值
     * @return
     */
    private boolean ruleResult(String operator, String value, String triggerValue) {
        boolean result = false;
        // 操作符比较
        switch (operator) {
            case "=":
                result = value.equals(triggerValue);
                break;
            case "!=":
                result = !value.equals(triggerValue);
                break;
            case ">":
                if (isNumeric(value) && isNumeric(triggerValue)) {
                    result = Double.parseDouble(value) > Double.parseDouble(triggerValue);
                }
                break;
            case "<":
                if (isNumeric(value) && isNumeric(triggerValue)) {
                    result = Double.parseDouble(value) < Double.parseDouble(triggerValue);
                }
                break;
            case ">=":
                if (isNumeric(value) && isNumeric(triggerValue)) {
                    result = Double.parseDouble(value) >= Double.parseDouble(triggerValue);
                }
                break;
            case "<=":
                if (isNumeric(value) && isNumeric(triggerValue)) {
                    result = Double.parseDouble(value) <= Double.parseDouble(triggerValue);
                }
                break;
            case "contain":
                result = value.contains(triggerValue);
                break;
            case "notcontain":
                result = !value.contains(triggerValue);
                break;
            default:
                break;
        }
        return result;
    }

    /**
     * 判断字符串是否为整数或小数
     */
    private boolean isNumeric(String str) {
        Pattern pattern = compile("[0-9]*\\.?[0-9]+");
        Matcher isNum = pattern.matcher(str);
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }

}
